// 使用一个泛型类型
#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    // 关联函数
    fn new(x: T, y: T) -> Self {
        Point { x, y }
    }

    // 方法
    fn get(&self) -> (&T, &T) {
        (&self.x, &self.y)
    }
}

// 泛型结构体，在impl时可以指定类型，对指定类型进行实现
impl Point<i32> {
    fn add(&self, other: &Self) -> Self {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

// 使用多个泛型类型
#[derive(Debug)]
struct Car<T, E> {
    name: T,
    price: E,
}

// 引用类型结合泛型的使用，语法上同样需要配合生命周期
// 特点一，这里解释下结构体后的泛型T与字段后的泛型&'a T的关系，首先结构体的T只用来表明类型（实例化时确定），字段后的&'a T则表明传进来时一个类型的引用，字段管借用（操作）、生命周期、类型，而结构体的泛型只管类型，所以就不是字段表明&T，结构体后的泛型也要表明&T
#[derive(Debug)]
struct Card<'a, T> {
    name: &'a T,
    id: u32,
}

fn swap<T>(x: T, y: T) -> (T, T) {
    (y, x)
}

// const泛型，用值来做为泛型参数
fn display_array<T, const N: usize>(arr: &[T; N])
where
    T: std::fmt::Debug,
{
    println!("arr: {:?}", arr);
}

fn foo<const N: usize>() {}

fn bar<const N: usize>() {
    // 这里的N是一个常量，可以在函数体中使用
    foo::<N>(); // ok

    // 这里的N不能参与表达式运算
    // foo::<{ N + 1 }>(); // error
}

// 常量泛型参数可以是任何类型，包括bool、char、float
fn check<const CHECK: bool>() {}

fn console<const LOG: char>() {}

fn main() {
    // 结构体
    // 隐式写法
    let point = Point { x: 5, y: 10 };
    println!("point: {:?}", point);
    // 显示写法
    let point = Point::<f64> { x: 1.2, y: 1.3 };
    println!("point: {:?}", point);
    // 结构体的方法
    let p = point.get();
    print!("point: {:?}", p);

    let car = Car {
        name: String::from("BYD"),
        price: 240_000,
    };
    println!("car: {:?}", car);

    // 特点二，这里直接拿&str给到Card实例化，会存在泛型拿到str类型，对此不能确定类型的大小（Sized Trait），泛型是不接受的，编译也不会通过，虽然看似满足了Card.name的传值要求
    // let name = "daniel";
    // let card = Card {
    //     name: name,
    //     id: 420,
    // };

    let name = "danile";
    let card = Card {
        name: &name,
        id: 420,
    };
    println!("card: {:?}", card);

    // 函数
    // 普通函数
    // 这种是隐式表达，属于自动推导的
    let p = swap(5, 10);
    println!("p: {:?}", p);
    // 以下两种是显式表达，属于自动表明的
    let p = swap::<i32>(5, 10);
    println!("p: {:?}", p);
    let p: (f64, f64) = swap(1.0, 2.0);
    println!("p: {:?}", p);

    // 关联函数
    // 隐式写法
    let point = Point::new(1, 2);
    println!("point: {:?}", point);
    // 显示写法，比较复杂，是遵循在结构体上先声明泛型，然后作用到关联函数上，而不是去关联函数后头声明泛型
    let point = Point::<f64>::new(1.0, 2.0);
    println!("point: {:?}", point);

    // 对泛型结构体指定类型实现方法
    // 以下p1、p2和p3均可调用add方法
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point::new(1, 2);
    let p3 = p1.add(&p2);
    println!("p1 + p2 = {:?}", p3);

    // 以下p1和p2无法调用add方法，因为add方法只在i32上实现过
    let p1 = Point { x: 1.1, y: 2.2 };
    let p2 = Point::new(1.1, 2.2);
    println!("p1 = {:?}; p2 = {:?}", p1, p2);

    // const泛型
    let arr = [1, 2, 3];
    display_array(&arr);

    foo::<10>();

    // 在常量泛型中使用表达式
    foo::<{ 5 + 5 }>();
}
